﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System.Reflection;

namespace intelliEssay.Interop
{
    /// <summary>
    /// Provides Initialization of all types in OpenXml
    /// All methods in this class make use of reflection, so use sparingly.
    /// </summary>
    public static class InitializationServices
    {
        static Dictionary<Type, List<Type>> mergeExclusiveMemberDictionary = new Dictionary<Type, List<Type>>();
        public static void RegisterMergeExclusiveMemberInformation(Type containerType, params Type[] exclusiveMembers)
        {
            if (mergeExclusiveMemberDictionary.ContainsKey(containerType) == false)
            {
                mergeExclusiveMemberDictionary.Add(containerType,new List<Type>());
            }
            var exclusiveList = mergeExclusiveMemberDictionary[containerType];
            exclusiveList.AddRange(exclusiveMembers);
        }
        /// <summary>
        /// 初始化变量的成员。不进行递归。有时，可能需要多次调用该函数以完成初始化。这个函数有可能导致效率问题。
        /// 数组的初始化不可以完成。
        /// </summary>
        /// <param name="objectRef">这个变量的property需要初始化</param>
        /// <param name="propertyNames">需要初始化property的名字的集合，大小写敏感</param>
        /// <param name="correspondingValues">初始化后的值</param>
        /// <returns></returns>
        public static object GetObjectInitialized(object objectRef, string[] propertyNames, object[] correspondingValues)
        {
            object result = Activator.CreateInstance(objectRef.GetType());
            if (objectRef == null)
                throw new ApplicationException("objectRef参数为NULL");
            if (propertyNames.Length != correspondingValues.Length)
                throw new ApplicationException("需要初始化的属性个数和传入的值的个数不相等");
            for (int i = 0; i < propertyNames.Length; i++)
            {
                object origValue = objectRef.GetType().GetProperty(propertyNames[i]).GetValue(objectRef, null);
                if (origValue != null)
                {
                    try
                    {
                        result.GetType().GetProperty(propertyNames[i]).SetValue(result, origValue, null);
                    }
                    catch
                    {
                        object DeepCopy = null;
                        Value_Priority_Pair vpp = new Value_Priority_Pair() { value = origValue, priority = 0 };
                        MergeProperties(ref DeepCopy, new Value_Priority_Pair[] { vpp });
                        result.GetType().GetProperty(propertyNames[i]).SetValue(result, DeepCopy, null);
                    }
                }
                else
                {
                    result.GetType().GetProperty(propertyNames[i]).SetValue(result, correspondingValues[i], null);
                }
            }
            return result;
        }
        /// <summary>
        /// This method operates merge on element based on priority values.
        /// Reflection is invoked to examine each writable properties and values are.
        /// NOTE properties defined by OpenXmlElement are not merged, since these properties have state issues.
        /// </summary>
        /// <param name="mergedObject">The merged element</param>
        /// <param name="mergeParticipants">The participants of the merge operation</param>
        public static void MergeProperties(ref dynamic mergedObject, IEnumerable<Value_Priority_Pair> mergeParticipants)
        {
            if (mergeParticipants == null) return;
            if (mergeParticipants.Count() == 0) return;
            var mergeList = mergeParticipants.ToArray();
            for (int i = 0; i < mergeList.Length; i++)
            {
                if ((mergeList[i].value.GetType() != mergeList[0].value.GetType()))
                {
                    throw new InvalidOperationException("mergeParticipants should be of same types.");
                }
            }
            mergedObject = Activator.CreateInstance(mergeParticipants.First().value.GetType());
            object o = mergeParticipants.First<Value_Priority_Pair>().value;
            IEnumerable<PropertyInfo> piA = o.GetType().GetProperties().Where<PropertyInfo>(x =>
                 {
                     return x.MemberType == System.Reflection.MemberTypes.Property && x.CanWrite && x.IsSpecialName == false && x.DeclaringType != typeof(OpenXmlElement) && x.CanRead && !x.Name.Contains("Xml");
                 });
            foreach (PropertyInfo pi in piA)
            {
                try
                {
                    Type containerType = mergedObject.GetType();

                    var newValue = mergeParticipants.Where<Value_Priority_Pair>(x =>
                         {
                             return x.value != null && x.value.GetType().GetProperty(pi.Name).GetValue(x.value, null) != null;
                         });
                    dynamic highPrioValue = null;
                    if (newValue != null && newValue.Count() != 0)
                    {
                        highPrioValue = newValue.Max<Value_Priority_Pair>().value;
                    }
                    pi.SetValue(mergedObject, pi.GetValue(highPrioValue, null), null);
                }
                catch
                {
                    continue;
                }
            }
        }


    }

}
